package jf.collections;

import jf.closure.F1;
import jf.closure.F1B;

import java.util.Comparator;
import java.util.List;
import java.util.Collection;

/**
 * @author ivanalx
 * @date 15.05.2009 14:40:23
 */
public interface ListX<A> extends List<A>, CollectionX<A> {

	public A getRight(int index);

	/**
	 * Returns all the elements of this list that satisfy the predicate filter.
	 * The order of the elements is preserved.
	 * It is guaranteed that the receiver list itself is returned if all its elements satisfy the predicate `p'.
	 * Hence the following equality is valid: (xs filter p) eq xs == xs forall p
	 *
	 * @param filter closure for filter
	 * @return filtered list
	 */
	public ListX<A> filter(F1B<A> filter);

	/**
	 * Returns the list resulting from applying the given function f to each element of this list.
	 *
	 * @param transformer
	 * @param <B>
	 * @return
	 */
	public <B> ListX<B> map(F1<A, B> transformer);

	public ListX<A> addX(A a);

	public ListX<A> addX(Collection<? extends A> a);

	public ListX<A> addX(A... a);

	public ListX<A> removeX(A a);

	public ListX<A> removeX(A... a);

	public ListX<A> removeX(Collection<? extends A> a);

	public ListX<A> removeAtX(Integer... at);

	public ListX<A> removeAtX(Collection<Integer> a);

	public ListX<A> add2This(A a);

	public ListX<A> add2This(A... a);

	public ListX<A> add2This(Collection<? extends A> a);

	public ListX<A> add2This(int index, A a);
	public ListX<A> add2This(int index, A... a);
	public ListX<A> add2This(int index, Collection<? extends A> a);

	public boolean add(int index, A... col);

	public ListX<A> removeAtFromThis(Integer... at);
	public ListX<A> removeAtFromThis(Collection<Integer> a);
	/**
	 * Take a slice from current list and put in into new one
	 * @param from start index of slice
	 * @param to end index of slice
	 * @return list of current list type
	 */
	public ListX<A> slice(int from, int to);

	/**
	 * @return a list without its n first elements.
	 *         If this list has less than n elements, empty list is returned.
	 */
	public ListX<A> drop(int n);

	/**
	 * Returns the list without its rightmost n elements.
	 */
	public ListX<A> dropRight(int n);

	/**
	 * Drop elements from list while predicate is true
	 *
	 * @param f
	 * @return
	 */
	public ListX<A> dropWhile(F1B<A> f);

	/**
	 * Applies the given function f to each element of this list, then concatenates the results.
	 *
	 * @param f
	 * @param <B>
	 * @return
	 */
	public <B> ListX<B> flatMap(F1<A, Iterable<B>> f);

	/**
	 * Todo think about generify it
	 *
	 * @param <B>
	 * @return
	 */
	public <B> ListX<B> flatten();

	/**
	 * Remove all elements that satisfy the predicate filter
	 * @param f
	 * @return
	 */
	public ListX<A> remove(F1B<A> f);

	/**
	 * Create a list without duplicates
	 *
	 * @return
	 */
	public ListX<A> removeDuplicates();

	/**
	 * Unsafe cast list to wide one.
	 *
	 * Use case: If you have List<Numbers>, but sure that in real it contains Integers. You can transform from List<Number> to
	 * List<Integer> with this method without any casting.
	 *
	 * @param <S>
	 * @return
	 */
	public <S extends A> ListX<S> unsafeWiden();


	/**
	 * Casting from list of one type to any another.
	 * @param <S>
	 * @return
	 */
	public <S> ListX<S> unsafeCastToOtherList();

	/**
	 * Safe cast list of current generic to list of any parent.
	 * @param <C>
	 * @return
	 */
	public <C extends ListX<? super A>> C safe();

	
	public ListX<A> sort();

	public ListX<A> sort(Comparator<A> c);

	/**
	 * @return this list without its first element.
	 */
	public ListX<A> tail();

	/**
	 * @return this list without its last element.
	 */
	public ListX<A> begin();

	/**
	 * Returns a list formed from this list and the specified list that by associating each element of the former with the element at the same position in the latter. If one of the two lists is longer than the other, its remaining elements are ignored.
	 *
	 * @param that
	 * @param <B>
	 * @return
	 */
	public <B> ListX<Tuple2<A, B>> zip(List<B> that);

	public <B> ListX<Tuple2<A, B>> zip(F1<A, B> f);
	/**
	 * @return a list that pairs each element of this list with its index, counting from 0.
	 */
	public ListX<Tuple2<A, Integer>> zipWithIndex();

	/**
	 * Take last n elements from list
	 *
	 * @param n
	 * @return
	 */
	public ListX<A> takeRight(int n);

	/**
	 * Take first n elements from list
	 *
	 * @param n
	 * @return
	 */
	public ListX<A> takeLeft(int n);

	/**
	 * @param f
	 * @return the longest prefix of this list whose elements satisfy the predicate f.
	 */
	public ListX<A> takeWhile(F1B<A> f);

	/**
	 * Returns the longest prefix of the list whose elements all satisfy the given predicate,
	 * and the rest of the list.
	 *
	 * @param f
	 * @return
	 */
	public Tuple2<ListX<A>, ListX<A>> span(F1B<A> f);

	/**
	 * Return new ListX instance in reversed order
	 * @return
	 */
	public ListX<A> reverse();

}
